package org.snlab.compiler;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import com.fasterxml.uuid.Generators;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.jgrapht.Graphs;
import org.snlab.proto.Ddmpt.Entry;

public class Compiler {
    private static String outputDir = "";
    public static Topology topology;
    public static String envDir;
    private DDMConf ddmConf = new DDMConf();

    public void output() {
        ddmConf.dump(envDir + "/gen/ddmconf.yaml");
    }

    public void compile(DFA dfa) {
        // for (DFANode node : dfa.getGraph().vertexSet()) {
        //     VnodeConf vnodeConf = new VnodeConf(node.getUuid(), node.getDevice().getName());

        //     List<DFANode> downs = Graphs.successorListOf(dfa.getGraph(), node);
        //     for (DFANode down : downs) {
        //         DownstreamConf downstreamConf = new DownstreamConf(down.getUuid(), down.getDevice().getName(),
        //                 down.getDevice().getAddr());
        //         Set<Link> links = Compiler.topology.getGraph().getAllEdges(node.getDevice(), down.getDevice());
        //         downstreamConf.setVia(links.stream().map(l -> l.getPort(node.getDevice())).toList());
        //         vnodeConf.getDownstreamConfs().add(downstreamConf);
        //     }
        //     ddmConf.getVnodeConfs().add(vnodeConf);
        // }
    }

    public void compile(Collection<Requirement> reqs) {
        // for (Requirement req : reqs) {

        //     DDMConf ddmConf = new DDMConf();

        //     for (DFANode node : req.getDfa().getGraph().vertexSet()) {
        //         VnodeConf vnodeConf = new VnodeConf(node.getUuid(), node.getDevice().getName());

        //         List<DFANode> downs = Graphs.successorListOf(req.getDfa().getGraph(), node);
        //         for (DFANode down : downs) {
        //             DownstreamConf downstreamConf = new DownstreamConf(down.getUuid(), down.getDevice().getName(),
        //                     down.getDevice().getAddr());
        //             Set<Link> links = Compiler.topology.getGraph().getAllEdges(node.getDevice(), down.getDevice());
        //             downstreamConf.setVia(links.stream().map(l -> l.getPort(node.getDevice())).toList());
        //             vnodeConf.getDownstreamConfs().add(downstreamConf);
        //         }
        //         ddmConf.getVnodeConfs().add(vnodeConf);
        //     }
        //     ddmConf.dump(outputDir);
        // }
    }

    public byte[] compile(Topology topo, String... reqs) throws IOException {
        Compiler.topology = topo;
        List<Requirement> requirements = new ArrayList<>();
        for (String r : reqs) {
            Requirement req = new Requirement();
            // hard code for test
            DFA dfa = new DFA();
            Set<DFANode> nodes = new HashSet<>();
            DFANode n1 = new DFANode(topo.getDeviceByName("wash"), dfa);
            DFANode n2 = new DFANode(topo.getDeviceByName("atla"), dfa);
            DFANode n3 = new DFANode(topo.getDeviceByName("hous"), dfa);
            DFANode n4 = new DFANode(topo.getDeviceByName("losa"), dfa);
            nodes.add(n1);
            nodes.add(n2);
            nodes.add(n3);
            nodes.add(n4);
            dfa.newStates(nodes);
            dfa.setStart(n1);
            dfa.setAccept(n4);
            dfa.newTransfer(n1, n2, "atla");
            dfa.newTransfer(n2, n3, "hous");
            dfa.newTransfer(n3, n4, "losa");
            req.setDfa(dfa);
            req.setHs(1);
            requirements.add(req);

            DDMConf ddmConf = new DDMConf();

            for (DFANode node : req.getDfa().getGraph().vertexSet()) {
                VnodeConf vnodeConf = new VnodeConf(node.getUuid(), node.getDevice().getName());

                List<DFANode> downs = Graphs.successorListOf(req.getDfa().getGraph(), node);
                for (DFANode down : downs) {
                    DownstreamConf downstreamConf = new DownstreamConf(down.getUuid(), down.getDevice().getName(),
                            down.getDevice().getAddr());
                    Set<Link> links = topo.getGraph().getAllEdges(node.getDevice(), down.getDevice());
                    // downstreamConf.setVia(links.stream().map(l -> l.getPort(node.getDevice())).toList());
                    vnodeConf.getDownstreamConfs().add(downstreamConf);
                }
                ddmConf.getVnodeConfs().add(vnodeConf);
            }
            ddmConf.dump(outputDir);

            // this.genNodeConfig();
        }

        return null;
    }


    public static void main1(String[] args) throws IOException {
        Compiler.topology = Topology.fromFile("envs/i2/topo.txt");
        outputDir = "envs/i2";
        Compiler compiler = new Compiler();
        compiler.compile(topology, "reqs");
    }

    public static void main(String[] args) {
        Options options = new Options();
        // options.addRequiredOption("t", "topo", true, "The topology file");
        // options.addOption("r", true, "The requirement file");
        // options.addOption("o", true, "The output dir");
        // options.addRequiredOption("d", "addr", true, "The ddmd address info file");
        // options.addRequiredOption("dfa", "dfa", true, "The DFA file");
        options.addRequiredOption("e", "env", true, "The env dir");
        CommandLineParser parser = new DefaultParser();
        CommandLine cmd;
        try {
            cmd = parser.parse(options, args);
            envDir = cmd.getOptionValue("e");
            // if (cmd.hasOption("o")) {
            // outputDir = cmd.getOptionValue("o");
            // }

            Compiler.topology = Topology.fromFile(envDir + "/topo.txt");
            Compiler.topology.assignAddrFromFile(envDir + "/gen/ddmdconfs.yaml");
            Compiler compiler = new Compiler();
            File dfaFiles[] = new File(envDir + "/gen/dfas").listFiles();
            for (File file : dfaFiles) {
                DFA dfa = DFA.fromFile(file.getAbsolutePath());
                compiler.compile(dfa);
            }
            compiler.output();
            // Requirement requirement = new Requirement();
            // DFA dfa = DFA.fromFile(cmd.getOptionValue("dfa"));
            // requirement.setDfa(dfa);
            // compiler.compile(new HashSet<>(Arrays.asList(requirement)));

            // compiler.compile(topology, "reqs");
        } catch (ParseException e) {
            System.err.println(e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}
